#define SYS_LOG_DOMAIN "main"
#include "sys_log.h"

#include "drivers/chip/_hal.h"

__aligned(sizeof(size_t) * 8) __no_init __used static u8_t _heap_mem[0x400000];

#define __HEAP_START &_heap_mem[0]
#define __HEAP_END &_heap_mem[sizeof(_heap_mem) / sizeof(_heap_mem[0])]
#define __STACK_SIZE 0

static int s_argc;
static const char **s_argv;

__weak int main_argc(void)
{
    return s_argc;
}

__weak const char **main_argv(void)
{
    return s_argv;
}

#if defined(MIX_COMMON)
#include <stdlib.h>
#include "heap/heap.h"
void _default_heap_init(void)
{
    heap_init(NULL, __HEAP_START, (uint)__HEAP_END - (uint)__HEAP_START - __STACK_SIZE);
}
#endif

#if defined(MIX_OS)

#include "os/os.h"

__weak int main(int argc, const char *argv[])
{
    s_argc = argc;
    s_argv = argv;

    drv_hal_init();

#if defined(MIX_COMMON) && defined(MIX_RTK)
    const int size = heap_block_max(NULL);
#else
    const int size = (uint)__HEAP_END - (uint)__HEAP_START - __STACK_SIZE - 0x400;
#endif
    return os_start(__HEAP_START, size);
}

#elif defined(MIX_RTK)

#include "rtk.h"

__weak int main(int argc, const char *argv[])
{
    s_argc = argc;
    s_argv = argv;

    drv_hal_init();

#if defined(MIX_COMMON)
    const int size = heap_block_max(NULL);
#else
    const int size = (uint)__HEAP_END - (uint)__HEAP_START - __STACK_SIZE - 0x400;
#endif
    return rtk_entry(malloc(size), size);
}

#elif defined(MIX_K_KIT)

#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <assert.h>

#include "kk.h"

static volatile int sys_time_counter;
static sigset_t xAllSignals;

static void timer_init(void)
{
    sigfillset(&xAllSignals);
    /* Don't block SIGINT so this can be used to break into GDB while
     * in a critical section. */
    sigdelset(&xAllSignals, SIGINT);
}

#if 1

static void sig_handler(int sig)
{
    sys_time_counter++;
}

static void timer_set(int usec)
{
    struct sigaction sa;
    struct itimerval itimer;
    int iRet;

    bzero(&sa, sizeof(sa));
    sa.sa_handler = sig_handler;
    iRet = sigaction(SIGALRM, &sa, NULL);
    SYS_ASSERT_FALSE(iRet, "getitimer");

    /* Initialise the structure with the current timer information. */
    iRet = getitimer(ITIMER_REAL, &itimer);
    SYS_ASSERT_FALSE(iRet, "getitimer");

    /* Set the interval between timer events. */
    itimer.it_interval.tv_sec = 0;
    itimer.it_interval.tv_usec = usec;

    /* Set the current count-down. */
    itimer.it_value.tv_sec = 0;
    itimer.it_value.tv_usec = usec;

    /* Set-up the timer interrupt. */
    iRet = setitimer(ITIMER_REAL, &itimer, NULL);
    SYS_ASSERT_FALSE(iRet, "setitimer");
}

static unsigned _port_interrupt_save(void)
{
    pthread_sigmask(SIG_BLOCK, &xAllSignals, NULL);
    return 0;
}
static void _port_interrupt_restore(unsigned nest)
{
    pthread_sigmask(SIG_UNBLOCK, &xAllSignals, NULL);
}

#else

#include <pthread.h>
#include <sched.h>
#include <unistd.h>

static pthread_t s_pthid;

static void *works(void *arg)
{
    int usec = (int)arg;
    struct timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 1000 * usec;
    for (;;)
    {
        sys_time_counter++;
        nanosleep(&ts, NULL);
    }

    return NULL;
}

static void timer_set(int usec)
{
    pthread_attr_t attr;
    struct sched_param param;
    int newprio = -20;

    pthread_attr_init(&attr);
    pthread_attr_getschedparam(&attr, &param);
    param.sched_priority = newprio;
    pthread_attr_setschedparam(&attr, &param);

    int res = pthread_create(&s_pthid, &attr, works, (void *)usec);
    if (res == -1)
    {
        perror("pthread_creat error");
        exit(1);
    }
    pthread_detach(s_pthid);
}

static unsigned _port_interrupt_save(void)
{
    return 0;
}
static void _port_interrupt_restore(unsigned nest)
{
}

#endif

static unsigned _port_get_sys_time(void)
{
    return sys_time_counter;
}
__weak int main(int argc, const char *argv[])
{
    s_argc = argc;
    s_argv = argv;

    drv_hal_init();

#if defined(MIX_COMMON)
    _default_heap_init();
#endif

    timer_init();
    timer_set(1000);

    static k_init_t const init_struct = {
        .malloc = __malloc,
        .free = __free,
        .get_sys_ticks = _port_get_sys_time,
        .interrupt_save = _port_interrupt_save,
        .interrupt_restore = _port_interrupt_restore,

        .scheduler_disable = NULL,
        .scheduler_enable = NULL,
        .get_work_q_hdl = NULL,
        .thread_sleep = NULL,
    };
    k_entry(&init_struct);

    return 0;
}

#else

#include <stdlib.h>

__weak int app_main(void)
{
    SYS_LOG_ERR("no app to run");
    return -1;
}

__weak int main(int argc, const char *argv[])
{
    s_argc = argc;
    s_argv = argv;

    drv_hal_init();

#if defined(MIX_COMMON)
    _default_heap_init();

    extern int none_entry(void);
    none_entry();
#else
    app_main();
#endif

    return 0;
}

#endif

void entry(void)
{
    main(0, NULL);
}

/* syscall ------------------------------------------------------------------------- */

__weak int drv_uart_poll_write(hal_id_t id, uint8_t data)
{
    return 0;
}

__weak bool drv_uart_wait_tx_busy(hal_id_t id)
{
    return 0;
}

static void _console_poll_write(char c)
{
    drv_uart_poll_write(0, c);
}

/**
 * @brief 对 printf.h 兼容
 */
__weak void _putchar(char character)
{
    _console_poll_write(character);
}
